using System;
using System.Text;

namespace Pegasus.Text
{
	/// <summary>
	/// This class is used to encode chars streams to a telnet ascii format.
	/// </summary>
	public class TelnetEncoding : System.Text.Encoding
	{
		/// <summary>
		/// Default Constructor
		/// </summary>
		public TelnetEncoding()
		{
		}

		/// <summary>
		/// Calculates the number of bytes required to encode a specified character array.
		/// </summary>
		/// <param name="chars">The character array to encode.</param>
		/// <param name="index">The starting index of the character array to encode.</param>
		/// <param name="count">The number of characters to encode.</param>
		/// <returns>The number of bytes required to encode the specified range of characters.</returns>
		public override int GetByteCount( char[] chars, int index, int count )
		{
			int charEndIndex = index + count;
			int bytes = 0;

			for( int x = index; x < charEndIndex; x++ )
			{
				if( chars[ x ] == '\n' || chars[ x ] == '\r' )
				{
					bytes++;
				}

				bytes++;
			}
            
			return bytes;
		}

		/// <summary>
		/// Encodes all or part of a specified String or character array into a byte array.
		/// </summary>
		/// <param name="chars">The character array to encode.</param>
		/// <param name="charIndex">The starting index of the character array to encode</param>
		/// <param name="charCount">The number of characters to encode.</param>
		/// <param name="bytes">The byte array where the resulting encoding is stored.</param>
		/// <param name="byteIndex">The starting index of the resulting encoding in the byte array.</param>
		/// <returns>The number of bytes stored in array bytes.</returns>
		public override int GetBytes( char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex )
		{
			int charEndIndex = charIndex + charCount;
			int bytePosition = byteIndex;
			int maxChars = chars.Length;

			for( int x = charIndex; x < charEndIndex; x++ )
			{
				// If this is not the last char in the array
				if( x < maxChars )
				{
					if( chars[ x ] == '\n' && chars[ x + 1 ] != '\r' )
					{
						// If this is a \n that is not followed by a \r then add a \r
						bytes[ bytePosition ] = (byte) '\n';
						bytes[ bytePosition + 1 ] = (byte) '\r';
						bytePosition += 2;
					}
					else if( chars[ x ] == '\r' && chars[ x + 1 ] != '\n' )
					{
						// If this is a \r that is not followed by a \n then traslate to \n\r
						bytes[ bytePosition ] = (byte) '\n';
						bytes[ bytePosition + 1 ] = (byte) '\r';
						bytePosition += 2;
					}
					else if( ( chars[ x ] == '\n' && chars[ x + 1 ] == '\r' ) ||
						( chars[ x ] == '\n' && chars[ x + 1 ] == '\r' ) )
					{
						// If this is a \n\r or a \r\n then translate to \n\r
						bytes[ bytePosition ] = (byte) '\n';
						bytes[ bytePosition + 1 ] = (byte) '\r';
						bytePosition += 2;
						x++;
					}
					else 
					{
						// Just a char
						bytes[ bytePosition ] = (byte) chars[ x ];
						bytePosition++;
					}
				}
				else
				{
					// If the last char is a \n or a \r then translate to \n\r
					if( chars[ x ] == '\n' && chars[ x ] == '\r' )
					{
						bytes[ bytePosition ] = (byte) '\n';
						bytes[ bytePosition + 1 ] = (byte) '\r';
						bytePosition += 2;
					}
					else
					{
						// Just a char
						bytes[ bytePosition ] = (byte) chars[ x ];
						bytePosition++;
					}
				}
			}
			
			return bytePosition - byteIndex;
		}

		/// <summary>
		/// Calculates the number of characters produced by decoding an array of bytes.
		/// </summary>
		/// <param name="bytes">The byte array to decode.</param>
		/// <param name="index">The starting index where decoding begins.</param>
		/// <param name="count">The number of bytes to decode.</param>
		/// <returns>The number of characters produced by decoding a range of bytes in the specified byte array.</returns>
		public override int GetCharCount( byte[] bytes, int index, int count )
		{
			int byteEndIndex = index + count;
			int maxBytes = bytes.Length; 
			int chars = 0;

			for( int x = index; x < byteEndIndex; x++ )
			{
				// If this is not the last byte in the array
				if( x < maxBytes )
				{
					if( ( bytes[ x ] == '\n' && bytes[ x + 1 ] == '\r' ) ||
						( bytes[ x ] == '\r' && bytes[ x + 1 ] == '\n' ) )
					{
						// We can consolitate these into a single \n
						chars--;
					}
				}

				chars++;
			}
            
			return chars;
		}

		/// <summary>
		/// Decodes a byte array into an array of characters.
		/// </summary>
		/// <param name="bytes">The byte array to decode.</param>
		/// <param name="byteIndex">The starting index of the byte array to decode.</param>
		/// <param name="byteCount">The number of bytes to decode.</param>
		/// <param name="chars">The character array where the resulting decoding is stored.</param>
		/// <param name="charIndex">The starting index of the resulting decoding in the character array.</param>
		/// <returns>The number of characters stored in the character array.</returns>
		public override int GetChars( byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex )
		{
			int byteEndIndex = byteIndex + byteCount;
			int charPosition = charIndex;
			int maxBytes = bytes.Length;

			for( int x = byteIndex; x < byteEndIndex; x++ )
			{
				// If this is not the last byte in the array
				if( x < maxBytes )
				{
					if( ( bytes[ x ] == '\n' && bytes[ x + 1 ] == '\r' ) ||
						( bytes[ x ] == '\r' && bytes[ x + 1 ] == '\n' ) )
					{
						// We can consolitate these into a single \n
						chars[ charPosition ] = '\n';
						x++;
					}
					else
					{
						chars[ charPosition ] = (char) bytes[ x ];
					}
				}
				else
				{
					// If the last char is just a \r then translate to \n
					if( bytes[ x ] == '\r' )
					{
						chars[ charPosition ] = '\n';
					}
					else
					{
						chars[ charPosition ] = (char) bytes[ x ];
					}
				}

				charPosition++;
			}
			
			return charPosition - charIndex;
		}

		/// <summary>
		/// Get the maximum number of bytes required to encode a given number of characters.
		/// </summary>
		/// <param name="charCount">The number of characters to encode.</param>
		/// <returns>The maximum number of bytes required for encoding a given number of characters.</returns>
		public override int GetMaxByteCount( int charCount )
		{
			return charCount * 2;
		}

		/// <summary>
		/// Get the maximum number of characters produced by decoding a given number of bytes
		/// </summary>
		/// <param name="byteCount">The number of bytes to encode.</param>
		/// <returns>The maximum number of characters produced by decoding a specified number of bytes.</returns>
		public override int GetMaxCharCount( int byteCount )
		{
			return byteCount;
		}
	}
}
